package model.tests;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Collection;

import model.collisions.CollisionCategory;
import model.collisions.Collisionable;
import model.elements.NonPlayerCharacter;
import model.elements.PlayerCharacter;
import model.elements.Wall;
import model.elements.ArtificialIntelligence.AlwaysGoRight;
import model.elements.ArtificialIntelligence.AlwaysUseWeapon;
import model.elements.attributes.Coordinate2D;
import model.elements.attributes.Damage;
import model.elements.attributes.Lives;
import model.elements.attributes.Speed;
import model.elements.commands.ElementCommand;
import model.elements.commands.ElementCommandFactory;
import model.elements.configurations.NonPlayerCharacterConfig;
import model.elements.states.movement.Stopped;
import model.elements.states.weapon.BombEquipped;
import model.exceptions.ModelExceptions;
import model.map.ElementsMap;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

// TODO: Auto-generated Javadoc
/**
 * The Class NonPlayerCharacterTest.
 */
public class NonPlayerCharacterTest {

	/** The test map. */
	private static ElementsMap	testMap		 = new ElementsMap(20, 20);
	
	/** The test non player character. */
	private NonPlayerCharacter 	testNonPlayerCharacter;
	
	/**
	 * The Class NPC_TEST_USE_WEAPON.
	 */
	public class NPC_TEST_USE_WEAPON extends NonPlayerCharacterConfig 
	{
		
		/**
		 * Instantiates a new npc test use weapon.
		 */
		public NPC_TEST_USE_WEAPON(  ) {
			m_lives 		 = new Lives(5);
			m_speed 		 = new Speed(1);
			m_equippedWeapon = new BombEquipped(3);
			m_IA 			 = new AlwaysUseWeapon(  );
			m_damage 		 = new Damage( 5 );
			m_category 		 = CollisionCategory.C_NPC;
		}
	}
	
	/**
	 * The Class NPC_TEST_MOVE_RIGHT.
	 */
	public class NPC_TEST_MOVE_RIGHT extends NonPlayerCharacterConfig
	{
		
		/**
		 * Instantiates a new npc test move right.
		 */
		public NPC_TEST_MOVE_RIGHT(  ) {
			m_lives 		 = new Lives(5);
			m_speed 		 = new Speed(1);
			m_equippedWeapon = new BombEquipped(3);
			m_IA 			 = new AlwaysGoRight(  );
			m_damage 		 = new Damage(5);
			m_category 		 = CollisionCategory.C_NPC;
		}
	}

	/**
	 * Sets the up before class.
	 *
	 * @throws Exception the exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {		
		ElementCommandFactory.setup(testMap);
	}
	
	/**
	 * Sets the up.
	 *
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception {
	}

	/**
	 * Tear down.
	 *
	 * @throws Exception the exception
	 */
	@After
	public void tearDown() throws Exception {
		testMap.clear();
	}

	/*
	 // DEPRECATED
	@Test
	public void testIsPlayerCharacter() throws ModelExceptions
	{
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		assertFalse( testNonPlayerCharacter.isPlayerCharacter() ); 
	}
    
	
	@Test
	public void testIsNonPlayerCharacter() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		assertTrue( testNonPlayerCharacter.isNonPlayerCharacter() ); 
	}
    */
	
	/**
	 * Test get position.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testGetPosition() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		Coordinate2D newPosition = new Coordinate2D(1,1);
		
		testNonPlayerCharacter.setPosition( newPosition );
		
		assertTrue( testNonPlayerCharacter.getPosition().equals( newPosition ) );
		
	}
	
	/**
	 * Test move to open space.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToOpenSpace() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		testNonPlayerCharacter.update();
		
		Coordinate2D finalPosition = testNonPlayerCharacter.getPosition();
		
		assertTrue( initialPosition.equals( Coordinate2D.minus( finalPosition, new  Coordinate2D(1,0) ) ) ); 
		
	}
	
	/**
	 * Test move to open out of bouds.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToOpenOutOfBouds() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 20,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		testNonPlayerCharacter.update();
		
		Coordinate2D finalPosition = testNonPlayerCharacter.getPosition();
		
		assertTrue( finalPosition.equals( initialPosition ) ); 
		
	}
	
	/**
	 * Test move to wall.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToWall() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );		
		
		testMap.addElement( new Wall( Coordinate2D.add( initialPosition, new Coordinate2D(1,0)) ) );
		
		testNonPlayerCharacter.update();
		
		Coordinate2D finalPosition = testNonPlayerCharacter.getPosition();
		
		assertTrue( finalPosition.equals( initialPosition ) ); 
		
	}
	
	/**
	 * Test drop bomb.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testDropBomb() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_USE_WEAPON(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		Collection<Collisionable> testList = testMap.getElementsIn( initialPosition );
		
		int initialCount = testList.size();
		
		testNonPlayerCharacter.update();
		
		testList = testMap.getElementsIn( initialPosition );
		
		int finalCount = testList.size();
		
		assertTrue( finalCount == ( initialCount + 1 ) ); 
		
	}
	
	/**
	 * Test move to npc.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToNPC() throws ModelExceptions {
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		testMap.addElement( new NonPlayerCharacter(  new Coordinate2D(1,0), new NPC_TEST_MOVE_RIGHT(  ) ) );
		
		int initialLives = testNonPlayerCharacter.getLives().getValue();
		
		testNonPlayerCharacter.update();
		
		Coordinate2D finalPosition = testNonPlayerCharacter.getPosition();
		
		int finalLives = testNonPlayerCharacter.getLives().getValue();
		
		assertTrue( finalPosition.equals( initialPosition ) ); 
		
		assertTrue( finalLives == initialLives );
				
		assertTrue( testNonPlayerCharacter.isAlive() );
		
	}
	
	/**
	 * Test move to player.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	@Test
	public void testMoveToPlayer() throws ModelExceptions {
		
		ElementCommand testPlayerCommand = ElementCommandFactory.getElementMove( new Stopped() );
		
		PlayerCharacter testPlayerCharacter = new PlayerCharacter(new Coordinate2D(1,0));
		
		testPlayerCharacter.addCommand( testPlayerCommand );
		
		testMap.addElement( testPlayerCharacter );		
		
		Coordinate2D initialPosition = new Coordinate2D( 0,0 );
		
		testNonPlayerCharacter = new NonPlayerCharacter( initialPosition , new NPC_TEST_MOVE_RIGHT(  )  );
		
		testMap.addElement( testNonPlayerCharacter );
		
		int initialLives = testNonPlayerCharacter.getLives().getValue();
		
		testNonPlayerCharacter.update();
		
		Coordinate2D finalPosition = testNonPlayerCharacter.getPosition();
		
		int finalLives = testNonPlayerCharacter.getLives().getValue();
		
		assertTrue( finalPosition.equals( new Coordinate2D(1,0) ) ); 
		
		assertTrue( finalLives == initialLives );
				
		assertTrue( testNonPlayerCharacter.isAlive() );		
		
		assertTrue( testPlayerCharacter.getLives().getValue() == 0 );
		
		assertFalse( testPlayerCharacter.isAlive() );
		
	}

}
